package com.xt.purchasingsystem.aspect;

import com.alibaba.fastjson.JSON;
import com.xt.purchasingsystem.annotation.PurchaseCache;
import com.xt.purchasingsystem.utils.ConvertUtil;
import com.xt.purchasingsystem.utils.Result;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**jjjaidnfasdf类
 * @author: 老姚
 * Date:  2020/5/18 11:12
 */
@Aspect
@Component
public class PurchaseCacheAspect {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;
//    @Autowired
//    private ThreadPoolExecutor threadPoolExecutor;

    @Around("@annotation(com.xt.purchasingsystem.annotation.PurchaseCache)")
    public Object cacheAroundAdvice(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        // 获取连接点签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        //获取目标方法的返回类型
        Class<?> returnType = method.getReturnType();
        // 获取连接点的GmallCache注解信息
        PurchaseCache purchaseCache = method.getAnnotation(PurchaseCache.class);
        //获取目标方法的参数列表
        Object[] args = point.getArgs();
        // 获取缓存的前缀
        String prefix = purchaseCache.prefix();
        // 组装成key
        String key = prefix + Arrays.asList(args).toString();

        //查询缓存
        result = this.cacheHit(key, returnType);
        if (result != null) {
            return result;
        }
        // 初始化分布式锁
        RLock lock = this.redissonClient.getLock("lock" + Arrays.asList(args).toString());
        // 防止缓存穿透 加锁
        lock.lock();

        // 再次检查内存是否有，因为高并发下，可能在加锁这段时间内，已有其他线程放入缓存
        result = this.cacheHit(key, returnType);
        if (result != null) {
            lock.unlock();
            return result;
        }
        // 2. 执行查询的业务逻辑从数据库查询
        result = point.proceed(args);
        //查询为空 数据库没有此数据 也要加入缓存 防止缓存穿透
        Result result1 = ConvertUtil.convertValue(result, Result.class);


        if (result1.getData() instanceof ArrayList&&result1.getData().toString().equals("[]")){
            this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result), 3, TimeUnit.MINUTES);
            lock.unlock();
            return result;
        }

//        if (result1.getData()==null){
//            System.out.println("对象为空");
//            this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result), 3, TimeUnit.MINUTES);
//            lock.unlock();
//            return result;
//        }

            this.redisTemplate.opsForValue().set(key, JSON.toJSONString(result), purchaseCache.timeout() + (int) (Math.random() * purchaseCache.random()), TimeUnit.MINUTES);

        // 释放锁
        lock.unlock();
        return result;
    }

    private Object cacheHit(String key, Class<?> returnType) {
        String json = redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(json)) {
            return JSON.parseObject(json, returnType);
        }
        return null;
    }

}
